package com.workcell.workdata.xthings.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.workcell.workdata.common.core.util.R;
import com.workcell.workdata.common.security.util.SecurityUtils;
import com.workcell.workdata.open.dto.WebSocketMessageDto;
import com.workcell.workdata.xthings.api.feign.RemoteFormDataService;
import com.workcell.workdata.xthings.api.feign.RemoteIotOpenService;
import com.workcell.workdata.xthings.constant.*;
import com.workcell.workdata.xthings.dto.XthingsTvVo;
import com.workcell.workdata.xthings.entity.CodeChainnum;
import com.workcell.workdata.xthings.entity.CodeTvBox;
import com.workcell.workdata.xthings.entity.DeviceRunLog;
import com.workcell.workdata.xthings.entity.TempBoard;
import com.workcell.workdata.xthings.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author 廖博文
 * @Date 2023/1/11 13:43
 * @Description
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class XthingsTvAppServiceImpl implements XthingsTvAppService {

	private final RedisTemplate redisTemplate;
	private final TempBoardService tempBoardService;
	private final CodeChainnumService codeChainnumService;
	private final RemoteFormDataService remoteFormDataService;
	private final RemoteIotOpenService remoteIotOpenService;
	private final DeviceRunLogService deviceRunLogService;
	private final CodeTvBoxService codeTvBoxService;
	private final String baseUrlBaseRedisKey = "XThingsTvUrl_%s";
	private final String updateBoardRedisKey = "XThingsTv_updateBoard_%s";

	/**
	 *
	 * （看板扫码）服务端：存放序列号扫码信息
	 *
	 */
	@Override
	public R saveTvIdentOfServer(String deviceNo) {
		CodeChainnum codeChainnum = codeChainnumService.getByChainnumAndHardwareType(deviceNo, HardwareTypeEnum.TV_BOX.getCode());
		if(null == codeChainnum){
			return R.failed("设备不存在。");
		}
		//redisTemplate.opsForValue().set(String.format(baseCodeBaseRedisKey,deviceNo), "1", 5 , TimeUnit.SECONDS);

		//清空上次选择的看板内容
		redisTemplate.delete(String.format(updateBoardRedisKey,deviceNo));
		WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
		Map<String, Object> data = new HashMap<>();
		data.put("result", WebSocketMessageCodeEnum.TVPICKBOARD.getMsg());
		webSocketMessageDto.setData(data);
		webSocketMessageDto.setToUserId(deviceNo);
		webSocketMessageDto.setFromUserId(String.valueOf(SecurityUtils.getUser().getId()));
		webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVPICKBOARD.getCode());
		try {
			R result = remoteIotOpenService.sendTo(webSocketMessageDto);
			if(R.failed().getCode() == result.getCode()){
				log.error("当前设备已离线-选择看板扫码失败{}。", webSocketMessageDto);
				return R.failed("当前设备已离线，请检查设备状态。");
			}
		} catch (Exception e) {
			log.error("选择看板扫码发生错误{}。", e);
		}
		return R.ok();
	}

	/**
	 * 服务端：查询该人看板配置
	 */
	@Override
	public R getTvBulletinBoardConfOfServer(String personnel) {
		LambdaQueryWrapper<TempBoard> tempBoardLambdaCondition = new QueryWrapper<TempBoard>()
				.lambda().orderByAsc(TempBoard::getSort);
		List<TempBoard> tempBoardList = tempBoardService.list(tempBoardLambdaCondition);
		List<Map<String, String>> boardConf = tempBoardList.stream().map(v->{
			Map<String, String> mapBoard= new HashMap<>(2);
			mapBoard.put("title",v.getName());
			mapBoard.put("path",v.getUrl());
			return mapBoard;
		}).collect(Collectors.toList());
		return R.ok(boardConf);
	}

	/**
	 * 服务端：发送选择的看板内容
	 */
	@Override
	public R sendTvBulletinBoardOfServer(String deviceNo, String url) {
		redisTemplate.opsForValue().set(String.format(baseUrlBaseRedisKey,deviceNo), url, 10 , TimeUnit.SECONDS);
		return R.ok();
	}

	//@Transactional(rollbackFor = Exception.class)
	//@SneakyThrows
	@Override
	public R saveUserPickRent(XthingsTvVo.UserPickRentVo userPickRentVo) {
		CodeChainnum codeChainnum = codeChainnumService.getByChainnumAndHardwareType(userPickRentVo.getDeviceNo(), HardwareTypeEnum.TV_BOX.getCode());
		if(null == codeChainnum){
			return R.failed("设备不存在。");
		}
		CodeChainnum updateCodeChinnum = new CodeChainnum();
		updateCodeChinnum.setId(codeChainnum.getId());
		updateCodeChinnum.setUseState(CodeChainnumUseStateEnum.ACTIVATED.getCode());
		updateCodeChinnum.setActiveTime(LocalDateTime.now());
		codeChainnumService.updateById(updateCodeChinnum);

		LambdaUpdateWrapper<CodeTvBox> onlineUpdateWrapper = new LambdaUpdateWrapper<>();
		onlineUpdateWrapper.eq(CodeTvBox::getChainNum, userPickRentVo.getDeviceNo());
		onlineUpdateWrapper.set(CodeTvBox::getRentId, userPickRentVo.getRentId());
		onlineUpdateWrapper.set(CodeTvBox::getRentName, userPickRentVo.getRentName());
		codeTvBoxService.update(onlineUpdateWrapper);

		WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
		Map<String, Object> data = new HashMap<>();
		data.put("state", "1");
		webSocketMessageDto.setData(data);
		webSocketMessageDto.setToUserId(userPickRentVo.getDeviceNo());
		webSocketMessageDto.setFromUserId(String.valueOf(SecurityUtils.getUser().getId()));
		webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVBINDRENT.getCode());
		R result = remoteIotOpenService.sendTo(webSocketMessageDto);
			if(R.failed().getCode() == result.getCode()){
			log.error("当前设备已离线-绑定租户失败{}。", webSocketMessageDto);
			//return R.failed("当前设备已离线，请检查设备状态。");
		}
		//操作日记
		DeviceRunLog deviceRunLog = new DeviceRunLog();
		deviceRunLog.setChainNum(userPickRentVo.getDeviceNo());
		deviceRunLog.setHardwareType(HardwareTypeEnum.TV_BOX.getCode());
		deviceRunLog.setDeviceNo(codeChainnum.getDeviceNo());
		deviceRunLog.setManufacturerCode(codeChainnum.getManufacturerCode());
		deviceRunLog.setEventType(DeviceRunEventEnum.OPERATION.getCode());
		deviceRunLog.setStartTime(LocalDateTime.now());
		deviceRunLog.setInputParam("TV绑定租户-"+userPickRentVo.getRentName()+"成功");
		deviceRunLogService.save(deviceRunLog);
		return R.ok();
	}


	/**
	 * 面板不分类（原版）
	 * @param name
	 * @param deviceNo
	 * @param size
	 * @param userId
	 * @return
	 */
    /*@Override
	public R getSmartOSBoardlist(String name, String deviceNo, Integer size, Long userId) {
		Long tenantId = codeTvBoxService.getBindRent(deviceNo);
		if(null == tenantId){
			return R.failed("设备未绑定租户，请先绑定。");
		}

		R result = new R();
		try {
			result = remoteFormDataService.getAppData(name, tenantId, userId);
		} catch (Exception e) {
			return R.failed("请先配置面板。");
		}
		if(ObjectUtil.isEmpty(result.getData()) || result.getCode() == R.failed().getCode() ){
			return R.failed("用户没有权限或租户没有配置面板。");
		}
		List<Map<String, String>> list = (List<Map<String, String>>) result.getData();
		List<XthingsTvVo.SmartOSBoardVo> boardConfUrl1 = Lists.newArrayList();
		List<XthingsTvVo.SmartOSBoardVo> boardConfUrl2 = Lists.newArrayList();
		List<XthingsTvVo.SmartOSBoardVo> boardConfUrl3 = Lists.newArrayList();
		Map<String, Object> boardConfUrData = new HashMap<>();
		List<XthingsTvVo.SmartOSBoardVo> finalBoardConfUrl = boardConfUrl3;
		list.forEach(v->{
			XthingsTvVo.SmartOSBoardVo mapBoard= new XthingsTvVo.SmartOSBoardVo();
			mapBoard.setName(v.get("name"));
			mapBoard.setId(Long.valueOf(v.get("id")));
			mapBoard.setRentId(Long.valueOf(v.get("tenant_id")));
			if(null != size && size == 3){
				List<String> url = Lists.newArrayList();
				url.add(v.get("url3") == "" ? null : v.get("url3"));
				mapBoard.setUrl(url);
				finalBoardConfUrl.add(mapBoard);
			}else{
				XthingsTvVo.SmartOSBoardVo mapBoard1 = mapBoard;
				List<String> url1 = Lists.newArrayList();
				url1.add(v.get("url1") == "" ? null : v.get("url1"));
				mapBoard1.setUrl(url1);
				boardConfUrl1.add(mapBoard1);

				XthingsTvVo.SmartOSBoardVo mapBoard2 = new XthingsTvVo.SmartOSBoardVo();
				mapBoard2.setName(v.get("name"));
				mapBoard2.setId(Long.valueOf(v.get("id")));
				mapBoard2.setRentId(Long.valueOf(v.get("tenant_id")));
				List<String> url2 = Lists.newArrayList();
				url2.add(v.get("url2") == "" ? null : v.get("url2"));
				mapBoard2.setUrl(url2);
				boardConfUrl2.add(mapBoard2);
			}
		});
		if(CollectionUtil.isNotEmpty(boardConfUrl3)){
			boardConfUrl3 = boardConfUrl3.stream().filter(p -> CollectionUtil.isNotEmpty(p.getUrl()) && !p.getUrl().contains(null)).collect(Collectors.toList());
			boardConfUrl3 = boardConfUrl3.stream()
					.collect(Collectors.toMap(k->new HashSet<>(Collections.singleton(k.getName())), v->v, (v1, v2)->v1))
					.values().stream().collect(Collectors.toList());

			boardConfUrl3 = boardConfUrl3.stream()
					.collect(Collectors.toMap(k->new HashSet<>(k.getUrl()), v->v, (v1, v2)->v1))
					.values().stream().collect(Collectors.toList());
			return R.ok(boardConfUrl3);

		}else{
			boardConfUrData.put("boardConfUrl1", boardConfUrl1.stream().filter(p -> CollectionUtil.isNotEmpty(p.getUrl()) && !p.getUrl().contains(null)).collect(Collectors.toList()));
			boardConfUrData.put("boardConfUrl2", boardConfUrl2.stream().filter(p -> CollectionUtil.isNotEmpty(p.getUrl()) && !p.getUrl().contains(null)).collect(Collectors.toList()));
			return R.ok(boardConfUrData);
		}
	}*/


	/**
	 * 面板分类
	 * @param name
	 * @param deviceNo
	 * @param size
	 * @param userId
	 * @return
	 */
	@Override
	public R getSmartOSBoardlist(String name, String deviceNo, Integer size, Long userId) {
		Long tenantId = codeTvBoxService.getBindRent(deviceNo);
		if(null == tenantId){
			return R.failed("设备未绑定租户，请先绑定。");
		}

		R result = new R();
		try {
			if(null != size && size == 3){
				result = remoteFormDataService.getAppDataByTemplate(name, tenantId, userId);
				if(ObjectUtil.isEmpty(result.getData()) || result.getCode() == R.failed().getCode() ){
					return R.failed("用户没有权限或租户没有配置面板。");
				}
				List<Map<String, Object>> list = (List<Map<String, Object>>) result.getData();
				list.forEach(item->{
					List<Map<String, Object>> tableData = (List<Map<String, Object>>) item.get("tableData");
					tableData = tableData.stream()
							.filter(i-> StrUtil.isNotEmpty((String)i.get("url3")))
							.collect(Collectors.toList());
					tableData = tableData.stream()
							.collect(Collectors.toMap(k->new HashSet<>(Collections.singleton(k.get("name"))), v->v, (v1, v2)->v1))
							.values().stream().collect(Collectors.toList());
					tableData = tableData.stream()
							.collect(Collectors.toMap(k->new HashSet<>(Collections.singleton(k.get("url3"))), v->v, (v1, v2)->v1))
							.values().stream().collect(Collectors.toList());
					item.put("tableData", tableData);
				});
				return R.ok(list);
			}else{
				result = remoteFormDataService.getAppData(name, tenantId, userId);
				if(ObjectUtil.isEmpty(result.getData()) || result.getCode() == R.failed().getCode() ){
					return R.failed("用户没有权限或租户没有配置面板。");
				}
				List<Map<String, String>> list = (List<Map<String, String>>) result.getData();
				List<XthingsTvVo.SmartOSBoardVo> boardConfUrl1 = Lists.newArrayList();
				List<XthingsTvVo.SmartOSBoardVo> boardConfUrl2 = Lists.newArrayList();
				//List<XthingsTvVo.SmartOSBoardVo> boardConfUrl3 = Lists.newArrayList();
				Map<String, Object> boardConfUrData = new HashMap<>();
				list.forEach(v->{
					XthingsTvVo.SmartOSBoardVo mapBoard= new XthingsTvVo.SmartOSBoardVo();
					mapBoard.setName(v.get("name"));
					mapBoard.setId(Long.valueOf(v.get("id")));
					mapBoard.setRentId(Long.valueOf(v.get("tenant_id")));

					XthingsTvVo.SmartOSBoardVo mapBoard1 = mapBoard;
					List<String> url1 = Lists.newArrayList();
					url1.add(v.get("url1") == "" ? null : v.get("url1"));
					mapBoard1.setUrl(url1);
					boardConfUrl1.add(mapBoard1);

					XthingsTvVo.SmartOSBoardVo mapBoard2 = new XthingsTvVo.SmartOSBoardVo();
					mapBoard2.setName(v.get("name"));
					mapBoard2.setId(Long.valueOf(v.get("id")));
					mapBoard2.setRentId(Long.valueOf(v.get("tenant_id")));
					List<String> url2 = Lists.newArrayList();
					url2.add(v.get("url2") == "" ? null : v.get("url2"));
					mapBoard2.setUrl(url2);
					boardConfUrl2.add(mapBoard2);
				});
				boardConfUrData.put("boardConfUrl1", boardConfUrl1.stream().filter(p -> CollectionUtil.isNotEmpty(p.getUrl()) && !p.getUrl().contains(null)).collect(Collectors.toList()));
				boardConfUrData.put("boardConfUrl2", boardConfUrl2.stream().filter(p -> CollectionUtil.isNotEmpty(p.getUrl()) && !p.getUrl().contains(null)).collect(Collectors.toList()));
				return R.ok(boardConfUrData);
 			}
		} catch (Exception e) {
			return R.failed("请先配置面板。");
		}
	}


	@Override
	public R updateBoard(XthingsTvVo.BoardVo boardVo) {
		CodeChainnum codeChainnum = codeChainnumService.getByChainnumAndHardwareType(boardVo.getDeviceNo(), HardwareTypeEnum.TV_BOX.getCode());
		if(null == codeChainnum){
			return R.failed("设备不存在。");
		}
		List<XthingsTvVo.BoardDetail> boardDetails = boardVo.getUrlList();
		R<Object> failed = isLatestVersion(boardVo.getDeviceNo(), boardDetails.size(), 17);
		if (failed != null) return failed;
		redisTemplate.opsForValue().set(String.format(updateBoardRedisKey,boardVo.getDeviceNo()), boardVo, 20 , TimeUnit.SECONDS);
		WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
		webSocketMessageDto.setToUserId(boardVo.getDeviceNo());
		webSocketMessageDto.setFromUserId(String.valueOf(SecurityUtils.getUser().getId()));
		Map<String, Object> data = new HashMap<>();
		if(1 == boardDetails.size()){
			data.put("url", boardDetails.get(0).getUrl());
			data.put("title", boardDetails.get(0).getTitle());
			data.put("id", boardDetails.get(0).getId());
			webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVGETUPDATEBOARD.getCode());
		}else{
			data.put("url", boardDetails);
			data.put("playbackCycle", boardVo.getPlaybackCycle());
			webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVGETUPDATEBOARDS.getCode());
		}
		webSocketMessageDto.setData(data);
		try {
			R result = remoteIotOpenService.sendTo(webSocketMessageDto);
			if(R.failed().getCode() == result.getCode()){
				log.error("当前设备已离线-更新看板失败{}。", webSocketMessageDto);
				return R.failed("当前设备已离线，请检查设备状态。");
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("更新看板发生了错误{}。", e);
		}

		//新添或更新TV当前投放内容
		boardDetails.get(0).setPlaying(true);
		boardVo.setJsonUrlList(JSONUtil.toJsonStr(boardDetails));
		codeTvBoxService.updatePanel(boardVo);
		//操作日记
		DeviceRunLog deviceRunLog = new DeviceRunLog();
		deviceRunLog.setChainNum(boardVo.getDeviceNo());
		deviceRunLog.setHardwareType(HardwareTypeEnum.TV_BOX.getCode());
		deviceRunLog.setDeviceNo(codeChainnum.getDeviceNo());
		deviceRunLog.setManufacturerCode(codeChainnum.getManufacturerCode());
		deviceRunLog.setEventType(DeviceRunEventEnum.OPERATION.getCode());
		deviceRunLog.setStartTime(LocalDateTime.now());
		deviceRunLog.setInputParam("TV投放面板-"+boardVo.getJsonUrlList()+"成功");
		deviceRunLogService.save(deviceRunLog);
		return R.ok();
	}

	/**
	 * 判断tv版本
	 * @param deviceNo
	 * @param size
	 * @return
	 */
	private R<Object> isLatestVersion(String deviceNo, Integer size, Integer version) {
		if(size >1){
			String tvVersion = remoteIotOpenService.getTvVersion(deviceNo).getData();
			if(StringUtils.isEmpty(tvVersion) || version > Integer.parseInt(tvVersion)){
				return R.failed("当前版本不支持多轮播，请更新版本。");
			}
		}
		return null;
	}

	@Override
	public R saveSnScanCode(String deviceId) {
		//redisTemplate.opsForValue().set(String.format(snScanCodeRedisKey,deviceId), deviceId, 1 , TimeUnit.MINUTES);
		WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
		webSocketMessageDto.setToUserId(deviceId);
		webSocketMessageDto.setFromUserId(String.valueOf(SecurityUtils.getUser().getId()));
		webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVSNSCANCODE.getCode());
		try {
			R result = remoteIotOpenService.sendTo(webSocketMessageDto);
			if(R.failed().getCode() == result.getCode()){
				log.error("当前设备已离线-关联SN扫码失败{}。", webSocketMessageDto);
				return R.failed("当前设备已离线，请检查设备状态。");
			}
		} catch (Exception e) {
			log.error("关联SN扫码发生了错误{}。", e);
		}
		return R.ok();
	}

	@Override
	public R relatedDiveceNo(XthingsTvVo.UserPickRentVo userPickRentVo) {
		//redisTemplate.opsForValue().set(String.format(relatedDeviceRedisKey,deviceId), deviceNo, 60 , TimeUnit.SECONDS);
		//保存SN关联deviceId
		//redisTemplate.opsForValue().set(String.format(relatedSnAndDeviceIdKey, deviceNo), deviceId);
		//关联SN码
		CodeChainnum codeChainnum = codeChainnumService.getByChainnumAndHardwareType(userPickRentVo.getDeviceNo(), HardwareTypeEnum.TV_BOX.getCode());
		if(null == codeChainnum){
			return R.failed("设备不存在。");
		}
		WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
		Map<String, Object> data = new HashMap<>();
		data.put("result", WebSocketMessageCodeEnum.TVGETDEVICESN.getMsg());
		data.put("sn", userPickRentVo.getDeviceNo());
		webSocketMessageDto.setData(data);
		webSocketMessageDto.setToUserId(userPickRentVo.getDeviceId());
		webSocketMessageDto.setFromUserId(String.valueOf(SecurityUtils.getUser().getId()));
		webSocketMessageDto.setMessageCode(WebSocketMessageCodeEnum.TVGETDEVICESN.getCode());
		try {
			R result = remoteIotOpenService.sendTo(webSocketMessageDto);
			if(R.failed().getCode() == result.getCode()){
				log.error("当前设备已离线-关联SN失败{}。", webSocketMessageDto);
				return R.failed("绑定失败。");
			}
		} catch (Exception e) {
			log.error("关联SN发生了错误{}。", e);
		}
		//绑定租户
		saveUserPickRent(userPickRentVo);
		return R.ok();
	}

	@Override
	public R<Long> tvIsBind(String deviceNo) {
		CodeChainnum codeChainnum = codeChainnumService.getByChainnumAndHardwareType(deviceNo, HardwareTypeEnum.TV_BOX.getCode());
		if(null == codeChainnum){
			return R.failed("设备不存在。");
		}
		return R.ok(codeTvBoxService.getBindRent(deviceNo));
	}
}
